package tech.xs.common.framework.config;

import com.netflix.client.ClientException;
import tech.xs.common.framework.domain.constant.ResultConstant;
import tech.xs.common.framework.domain.exception.BaseException;
import tech.xs.common.framework.domain.enums.CommonResultEnum;
import tech.xs.common.framework.domain.exception.InvalidParameterException;
import tech.xs.common.framework.domain.model.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.internal.engine.path.NodeImpl;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import tech.xs.common.framework.util.ExceptionUtil;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.util.Set;

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局未知异常处理
     *
     * @param response Servlet响应
     * @param e        异常信息
     * @return 响应结果
     */
    @ExceptionHandler(value = Exception.class)
    public ApiResult<Object> basicExceptionHandler(HttpServletResponse response, Exception e) {
        Throwable cause = e.getCause();
        ApiResult<Object> result = ApiResult.unknownError();
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        if (cause instanceof ClientException) {
            log.error("服务调用异常:", cause);
            result.setMsg("服务调用异常:" + cause.getMessage());
        } else {
            log.error("全局异常处理 Exception", e);
        }
        return result;
    }

    /**
     * 自定义基本异常的处理
     *
     * @param e 异常信息
     * @return 返回接口对应的结果集
     */
    @ExceptionHandler(BaseException.class)
    public ApiResult<Object> baseExceptionHandler(BaseException e) {
        Integer code = e.getCode();
        if (code == null) {
            code = ResultConstant.UNKNOWN_ERROR_CODE;
        }
        ApiResult<Object> result = new ApiResult<>(code, e.getMessage());
        String msg = null;
        if (e.isPrint()) {
            log.error("全局异常处理 BaseException", e);
        }
        if (e.isResponseStackMsg()) {
            msg = ExceptionUtil.getStackMsg(e);
            result.setData(msg);
        }
        return result;
    }

    /**
     * 请求参数验证异常
     *
     * @param e 异常信息
     * @return 响应数据
     */
    @ExceptionHandler(InvalidParameterException.class)
    public ApiResult<Object> validatedInvalidParameterExceptionHandler(InvalidParameterException e) {
        return ApiResult.invalidParameter(e.getFieldName(), e.getMessage());
    }

    /**
     * 请求参数验证异常
     *
     * @param e 异常信息
     * @return 响应数据
     */
    @ExceptionHandler(BindException.class)
    public ApiResult<Object> validatedBindExceptionHandler(BindException e) {
        FieldError errorField = e.getFieldError();
        if (errorField != null) {
            return ApiResult.invalidParameter(errorField.getField(), errorField.getDefaultMessage());
        } else {
            return ApiResult.invalidParameter();
        }
    }

    /**
     * 请求参数验证异常
     *
     * @param e 异常信息
     * @return 响应数据
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public ApiResult<Object> constraintViolationExceptionHandler(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
        for (ConstraintViolation constraintViolation : constraintViolations) {
            Path propertyPath = constraintViolation.getPropertyPath();
            if (propertyPath instanceof PathImpl) {
                PathImpl path = (PathImpl) propertyPath;
                NodeImpl leafNode = path.getLeafNode();
                String message = constraintViolation.getMessage();
                return ApiResult.invalidParameter(leafNode.getName(), message);
            }
        }
        return new ApiResult<>(CommonResultEnum.invalidParameter.getValue(), e.getMessage(), null);
    }

    /**
     * 请求参数验证异常
     *
     * @param e 异常信息
     * @return 响应数据
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ApiResult<Object> validExceptionHandler(MethodArgumentNotValidException e) {
        FieldError errorField = e.getBindingResult().getFieldError();
        if (errorField != null) {
            return ApiResult.invalidParameter(errorField.getField(), errorField.getDefaultMessage());
        } else {
            return ApiResult.invalidParameter();
        }
    }

}
